﻿// ## 2023/09/20 # 铃 # 修复留存粮草量不足的运输bug
// ## 2023/05/20 # 铃 # 重写小城市运输的逻辑.
// ## 2023/03/11 # 铃 # 完成,AI可以把把武将送到没有武将但是有资源的小城,并把资源送到大城.
// ## 2022/11/11 # 铃 # 初稿,初衷是当小城市有钱有粮草,但是没有武将的时候,调度过来运输

namespace AI小城市运输
{
	const int 运输兵力下限 = 3000;
	const int 运输资金下限 = 2000;
	const int 运输兵粮下限 = 20000;
	const int 小城兵和兵粮比例 = 5;
	const int 兵装留存基础值 = 3000;
	const int 兵力留存基础值 = 3000;
	const int 留存兵力兵粮比 = 2;
	/// =========================================================================================

	const bool 调试模式 = false;

	const int KEY = pk::hash("AI小城市运输");

	class Main
	{
		pk::list<pk::person @> actors;

		Main()
		{
			pk::bind(202, pk::trigger202_t(onAIRunningOrder));
		}

		void onAIRunningOrder(pk::ai_context @context, pk::building @building, int cmd)
		{

			if (cmd == 据点AI_小城运输)
			{
				int building_force_id = building.get_force_id();
				pk::force @force = pk::get_force(building.get_force_id());
				pk::ai_context_base @base = context.base[building.get_id()];

				// 如果是大城市则不执行
				if (building.get_id() < 城市_末)
					return;

				if (base.status == 据点状态_战斗)
					return;

				if (pk::is_player_controlled(building))
					return;

				if (pk::get_idle_person_list(building).count == 0)
					return;

				if (building.energy < 80)
					return;

				if (pk::get_food(building) < 运输兵粮下限 or pk::get_troops(building) < 运输兵力下限)
					return;

				func_AI小城运输(force, building);
				ch::remove_idle_person(context, actors);
			}
		}

		void func_AI小城运输(pk::force @force, pk::building @building)
		{
			array<pk::building @> neighbor_building_arr = 获取目标城市列表(building);

			if (neighbor_building_arr.length == 0)
				return;

			// 运输候选城市的优先顺序
			neighbor_building_arr.sort(function(a, b) { return main.比较运输城市(a, b); });

			pk::building @neighbor_building = neighbor_building_arr[0];

			if (!pk::is_alive(neighbor_building))
				return;

			pk::list<pk::person @> person_list = pk::get_idle_person_list(building);
			if (person_list.count == 0)
				return;

			func_运输部队出征(building, neighbor_building);

			return;
		}

		void func_运输部队出征(pk::building @building, pk::building @neighbor_building)
		{
			int 金运输量;
			int 粮运输量;
			int 兵运输量;

			int 金留存量;
			int 粮留存量;
			int 兵留存量;

			bool 战斗 = ai::base_in_range_enemy_troops[building.get_id()] > 0;
			bool 危险 = ch::neibor_enemy_base_count(building, 1) >= 3;
			bool 前线 = ch::neibor_enemy_base_count(building, 1) >= 1;

			// 战斗据点
			if (战斗)
			{
				金留存量 = 5000;
				粮留存量 = 100000;
				兵留存量 = 50000;
			}

			// 危险据点
			if (危险)
			{
				金留存量 = 5000;
				粮留存量 = 80000;
				兵留存量 = 25000;
			}

			// 前线据点
			if (前线)
			{
				金留存量 = 3000;
				粮留存量 = 60000;
				兵留存量 = 20000;
			}
			else
			{
				金留存量 = 2000;
				粮留存量 = 30000;
				兵留存量 = 3000;
			}

			int person_count = pk::get_person_list(building, pk::mibun_flags(身份_君主, 身份_都督, 身份_太守, 身份_一般)).count;
			if (person_count > 1)
			{
				金留存量 += (person_count - 1) * 1000;
				兵留存量 += (person_count - 1) * 2000;
			}

			if (pk::get_food(building) < 粮留存量 + 3000 or pk::get_troops(building) < 兵留存量 + 2000)
				return;

			// 金运输量最简单,即使为0也没关系,初始值就可以作为终值

			int 初始金运输量 = pk::get_gold(building) - 金留存量;
			金运输量 = pk::max(初始金运输量, 0);

			// 初始兵运输量,兵运输量不能直接赋值,要看看粮草的情况

			int 初始兵运输量 = pk::get_troops(building) - 兵留存量;

			// 初始粮运输量,粮运输量也不能直接赋值

			int 初始粮运输量 = pk::get_food(building) - 粮留存量;
			if (初始粮运输量 < 5000)
				return;

			// 分情况讨论

			// pk::trace(pk::format("{},初始粮运输量：{}，粮留存量：{}", pk::decode(pk::get_name(building)), 初始粮运输量, 粮留存量));

			// 如果粮没有结余,则无论如何都不运输,因为很可能无法提供所需要的运输队粮
			if (pk::get_food(building) - 粮留存量 < 0)
				return;

			// 1.如果既不需要运粮,也不需要运金,也不需要运兵.则不动.
			if (初始金运输量 <= 0 and 初始粮运输量 <= 0 and 初始兵运输量 <= 0)
				return;

			// 2.如果不需要运兵,只需要运粮或者金,则运输500兵,同时附带至少3000粮.
			else if ((初始金运输量 > 0 or 初始粮运输量 > 0) and 初始兵运输量 <= 500)
			{
				兵运输量 = 500;
				粮运输量 = pk::max(初始粮运输量, 3000);
			}

			// 3.如果需要运兵,此次运输则不运粮,不然陷入循环计算
			// 而且这时候需要考虑据点的粮够不够吃,如果不足,则倒推最大可运兵力.
			// 需要给运输队留下粮食
			else if (初始兵运输量 > 500)
			{
				// 如果据点兵力比较多,要提高粮留存量,1000兵25粮一个旬度,因此1年粮草为兵力/1000*25*36
				粮留存量 = pk::max(粮留存量, pk::get_troops(building) / 1000 * 25 * 36);

				int 据点留存兵粮限制最大运输兵力 = (pk::get_food(building) - 粮留存量) * 10 / 2;
				兵运输量 = pk::min(初始兵运输量, 据点留存兵粮限制最大运输兵力);
				粮运输量 = 兵运输量 / 10 * 2;

				// 如果此时仍然剩下很多粮,则再计算一次运粮
				int 剩余粮 = pk::get_food(building) - 粮运输量;

				// 留下留存量,剩下的加到运输量里面去
				粮运输量 += pk::max(剩余粮 - 粮留存量, 0);
			}

			else
				return;

			// pk::trace(pk::format("{},金运输量：{}，粮运输量：{}，兵运输量：{}", pk::decode(pk::get_name(building)), 金运输量, 粮运输量, 兵运输量));

			pk::com_deploy_cmd_info cmd;
			@cmd.base = @building;
			cmd.type = 部队类型_运输;

			// 兵装运输
			int 兵装运输量 = 0;
			int 兵装留存量 = 0;
			int 兵装运输量_最大值 = 0;
			int 总兵装运输量 = 0;

			// 暂时分开考虑,每个兵装需要是兵力的0.6份则差不多(保留兵装选择的灵活性)
			兵装留存量 = int((pk::get_troops(building) - 兵运输量) * 0.6);

			for (int weapon_id = 兵器_枪; weapon_id <= 兵器_战马; weapon_id++)
			{

				兵装运输量_最大值 = pk::get_max_weapon_amount(neighbor_building, weapon_id) - pk::get_weapon_amount(neighbor_building, weapon_id);

				if (int(pk::get_weapon_amount(building, weapon_id)) > 兵装留存量)
					兵装运输量 = pk::get_weapon_amount(building, weapon_id) - 兵装留存量;
				else
					兵装运输量 = 0;

				if (兵装运输量 > 兵装运输量_最大值)
					兵装运输量 = 兵装运输量_最大值;

				if (weapon_id <= 兵器_战马 and 兵装运输量 > 0)
				{
					总兵装运输量 += 兵装运输量;
					cmd.weapon_id[weapon_id] = weapon_id;
					cmd.weapon_amount[weapon_id] = 兵装运输量;
				}
			}

			// 兵装运输
			int 兵器运输量 = 0;
			int 兵器留存量 = 0;
			int 兵器运输量_最大值 = 0;

			// 暂时分开考虑,每个10000兵需要1个兵器
			兵器留存量 = int((pk::get_troops(building) - 兵运输量) / 10000);

			for (int weapon_id = 兵器_冲车; weapon_id <= 兵器_斗舰; weapon_id++)
			{
				if (weapon_id == 兵器_走舸)
					continue;
				兵器运输量_最大值 = pk::get_max_weapon_amount(neighbor_building, weapon_id) - pk::get_weapon_amount(neighbor_building, weapon_id);

				if (int(pk::get_weapon_amount(building, weapon_id)) > 兵器留存量)
					兵器运输量 = pk::get_weapon_amount(building, weapon_id) - 兵器留存量;
				else
					兵器运输量 = 0;

				if (兵器运输量 > 兵器运输量_最大值)
					兵器运输量 = 兵器运输量_最大值;

				if (兵器运输量 > 0 and weapon_id != 兵器_走舸)
				{
					cmd.weapon_id[weapon_id] = weapon_id;
					cmd.weapon_amount[weapon_id] = 兵器运输量;
				}
			}

			// 运输量太少也没必要
			if ((金运输量 * 8 + 粮运输量 + 兵运输量 + 总兵装运输量) < 50000)
				return;

			// 不能撑满目标城市
			int 金运输量_最大值 = pk::min(pk::get_gold(building), pk::get_max_gold(neighbor_building) - pk::get_gold(neighbor_building));
			int 粮运输量_最大值 = pk::min(pk::get_food(building), pk::get_max_food(neighbor_building) - pk::get_food(neighbor_building));
			int 兵运输量_最大值 = pk::min(pk::get_troops(building), pk::get_max_troops(neighbor_building) - pk::get_troops(neighbor_building));

			// 避免意外情况损失惨重,每次运输都不超过上限
			金运输量_最大值 = pk::min(金运输量_最大值, 50000);
			粮运输量_最大值 = pk::min(粮运输量_最大值, 200000);
			兵运输量_最大值 = pk::min(兵运输量_最大值, 50000);

			pk::list<pk::person @> person_list = pk::get_idle_person_list(building);
			person_list.sort(function(a, b) { return a.stat[武将能力_统率] > b.stat[武将能力_统率]; });

			cmd.member[0] = person_list[0].get_id();

			actors.add(person_list[0]);

			cmd.food = pk::min(粮运输量_最大值, 粮运输量);
			cmd.gold = pk::min(金运输量_最大值, 金运输量);
			cmd.troops = pk::max(pk::min(兵运输量_最大值, 兵运输量), 500);
			cmd.order = 部队任务_移动;
			cmd.target_pos = neighbor_building.pos;

			int unit_id = pk::command(cmd);

			if (unit_id != -1)
			{
				pk::unit @unit = pk::get_unit(unit_id);
				unit.action_done = true;

				if (调试模式)
				{
					string unit_name = pk::format("\x1b[1x{}\x1b[0x", pk::decode(pk::get_name(unit)));
					string building_name = pk::format("\x1b[1x{}\x1b[0x", pk::decode(pk::get_name(building)));
					string neighbor_building_name = pk::format("\x1b[1x{}\x1b[0x", pk::decode(pk::get_name(neighbor_building)));
					string action_message = pk::format("{}从{}运输到{}", unit_name, building_name, neighbor_building_name);
					pk::force @force = pk::get_force(building.get_force_id());
					pk::message_box(pk::encode(action_message));
				}
			}
		}

		// 在运输部队出发的城市接壤的自势力城市中搜寻可运输的候选城市
		array<pk::building @> 获取目标城市列表(pk::building @building)
		{
			array<pk::building @> neighbor_building_arr;
			for (int i = 0; i < 据点_末; i++)
			{

				if (building.get_id() == i) continue;
				pk::building @neighbor_building = pk::get_building(i);

				if (neighbor_building.get_force_id() != building.get_force_id())
					continue;

				int distanceToBuilding = pk::get_building_distance(building.get_id(), i, building.get_force_id());

				if (distanceToBuilding > 1)
					continue;

				if (!pk::is_alive(neighbor_building))
					continue;
				if (neighbor_building.get_force_id() != building.get_force_id())
					continue;

				neighbor_building_arr.insertLast(neighbor_building);
			}

			return neighbor_building_arr;
		}

		int 附近敌方据点数量(pk::building @building, int distance)
		{
			int enemy_building_count = 0;
			for (int i = 0; i < 据点_末; i++)
			{
				pk::building @neighbor_building = pk::get_building(i);
				int distanceToBuilding = pk::get_building_distance(building.get_id(), i, building.get_force_id());
				if (distanceToBuilding > distance)
					continue;
				if (!pk::is_enemy(building, neighbor_building))
					continue;
				enemy_building_count++;
			}
			return enemy_building_count;
		}
		int 附近敌方据点兵力(pk::building @building, int distance)
		{
			int enemy_troops_count = 0;
			for (int i = 0; i < 据点_末; i++)
			{
				pk::building @neighbor_building = pk::get_building(i);
				int distanceToBuilding = pk::get_building_distance(building.get_id(), i, building.get_force_id());
				if (distanceToBuilding > distance)
					continue;
				if (!pk::is_enemy(building, neighbor_building))
					continue;
				enemy_troops_count += pk::get_max_marchable_troops(neighbor_building);
			}
			return enemy_troops_count;
		}

		int 据点周围敌军(pk::building @building, int distance)
		{
			int enemy_troops = 0;
			pk::list<pk::unit @> unit_list = pk::get_unit_list();
			for (int i = 0; i < unit_list.count; i++)
			{
				pk::unit @unit0 = unit_list[i];
				if (!pk::is_alive(unit0))
					continue;
				if (!pk::is_enemy(unit0, building))
					continue;

				if (pk::get_distance(building.get_pos(), unit0.get_pos()) < distance)
					enemy_troops += unit0.troops;
			}
			return enemy_troops;
		}
		// 使用多因子模型比较需要输送的城市之间的优先级
		bool 比较运输城市(pk::building @building_a, pk::building @building_b)
		{
			float score_a = 0.f;
			float score_b = 0.f;

			// 因子1:城市前线状态：敌人据点越多的优先级越高
			score_a += 附近敌方据点数量(building_a, 1);
			score_b += 附近敌方据点数量(building_b, 1);

			// 因子2:城市前线状态：敌军兵力越多的优先级越高
			score_a += 附近敌方据点兵力(building_a, 1) / 30000.f;
			score_b += 附近敌方据点兵力(building_b, 1) / 30000.f;

			// 因子3:城市战斗状态
			score_a += 据点周围敌军(building_a, 30) / 10000.f;
			score_b += 据点周围敌军(building_b, 30) / 10000.f;

			// 因子4:城市自身兵力
			score_a -= pk::get_troops(building_a) / 10000.f;
			score_b -= pk::get_troops(building_b) / 10000.f;

			// 因子5:城市资金
			score_a -= pk::get_gold(building_a) / 20000.f;
			score_b -= pk::get_gold(building_b) / 20000.f;

			// 因子6:城市兵粮
			score_a -= pk::get_food(building_a) / 50000.f;
			score_b -= pk::get_food(building_b) / 50000.f;

			return score_a > score_b;
		}
	} Main main;

}